package com.wq.app.painter.widget;

import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.RectF;
import android.os.Build;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;

import java.util.Vector;

import com.wq.app.painter.OnStrokeReceiveListener;
import com.wq.app.painter.StrokeDrawerThread;
import com.wq.app.painter.data.Stroke;
import com.wq.app.painter.data.WhiteBoardPage;
import com.wq.app.painter.widget.*;
import com.wq.app.painter.widget.DrawableViewSaveState;
import com.wq.app.painter.widget.StrokeConfig;
import com.wq.app.painter.widget.draw.WhitePageDrawer;
import com.wq.app.painter.widget.draw.log.LogPrinter;
import com.wq.app.painter.widget.gestures.creator.GestureCreator;
import com.wq.app.painter.widget.gestures.creator.GestureCreatorListener;
import com.wq.app.painter.widget.gestures.scale.GestureScaleListener;
import com.wq.app.painter.widget.gestures.scale.GestureScaler;
import com.wq.app.painter.widget.gestures.scale.ScalerListener;
import com.wq.app.painter.widget.gestures.scroller.GestureScrollListener;
import com.wq.app.painter.widget.gestures.scroller.GestureScroller;
import com.wq.app.painter.widget.gestures.scroller.ScrollerListener;

public class DrawableView extends SurfaceView implements OnStrokeReceiveListener, SurfaceHolder.Callback,
        View.OnTouchListener, ScrollerListener, GestureCreatorListener, ScalerListener {
    private int canvasHeight;
    private int canvasWidth;
    private WhiteBoardPage boardPage;
    private com.wq.app.painter.widget.draw.WhitePageDrawer whitePageDrawer;
    private StrokeDrawerThread drawerThread;

    private com.wq.app.painter.widget.gestures.scale.GestureScaler gestureScaler;
    private com.wq.app.painter.widget.gestures.creator.GestureCreator gestureCreator;
    private com.wq.app.painter.widget.gestures.scroller.GestureScroller gestureScroller;
    private GestureDetector gestureDetector;
    private ScaleGestureDetector scaleGestureDetector;

    public DrawableView(Context context) {
        super(context);
        init();
    }

    public DrawableView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public DrawableView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public DrawableView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init();
    }

    private void init() {
        gestureScroller = new com.wq.app.painter.widget.gestures.scroller.GestureScroller(this);
        gestureDetector = new GestureDetector(getContext(), new com.wq.app.painter.widget.gestures.scroller.GestureScrollListener(gestureScroller));
        gestureScaler = new com.wq.app.painter.widget.gestures.scale.GestureScaler(this);
        scaleGestureDetector = new ScaleGestureDetector(getContext(), new com.wq.app.painter.widget.gestures.scale.GestureScaleListener(gestureScaler));
        gestureCreator = new com.wq.app.painter.widget.gestures.creator.GestureCreator(this);
        whitePageDrawer = new com.wq.app.painter.widget.draw.WhitePageDrawer();
        boardPage = new WhiteBoardPage();

        setDrawingCacheEnabled(true);
        setOnTouchListener(this);
        getHolder().addCallback(this);
    }

    public void setConfig(com.wq.app.painter.widget.StrokeConfig config) {
        if (config == null) {
            throw new IllegalArgumentException("Paint configuration cannot be null");
        }
        canvasWidth = config.getCanvasWidth();
        canvasHeight = config.getCanvasHeight();
        gestureCreator.setConfig(config);
        gestureScaler.setZooms(config.getMinZoom(), config.getMaxZoom());
        gestureScroller.setCanvasBounds(canvasWidth, canvasHeight);
        whitePageDrawer.setConfig(config);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        gestureScroller.setViewBounds(w, h);
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        scaleGestureDetector.onTouchEvent(event);
        gestureDetector.onTouchEvent(event);
        gestureCreator.onTouchEvent(event);
//        LogPrinter.log(" DrawableView onTouch: " + event);
        return true;
    }

    public void undo() {
        Vector<Stroke> strokes = boardPage.getStrokes();
        if (strokes.size() > 0) {
            strokes.remove(strokes.size() - 1);
            invalidate();
        }
    }

    @Override
    public void draw(Canvas canvas) {
        long start = System.currentTimeMillis();
        super.draw(canvas);
        canvas.clipRect(0, 0, canvasWidth, canvasHeight);
        canvas.drawColor(com.wq.app.painter.widget.StrokeConfig.DEFAULT_BACKGROUND_COLOR);
        drawContent(canvas);
        long end = System.currentTimeMillis();

        long interval = end - start;
        if (0 != interval) {
            com.wq.app.painter.widget.draw.log.LogPrinter.log(" draw time interval: " + interval + " fps: " + (1000 / interval));
        }
    }

    public void drawContent(Canvas canvas) {
        whitePageDrawer.onDraw(canvas, boardPage, getDrawingCache());
    }

    public void clear() {
        boardPage.clear();
    }

    public Bitmap obtainBitmap(Bitmap createdBitmap) {
        return whitePageDrawer.obtainBitmap(createdBitmap, boardPage);
    }

    public Bitmap obtainBitmap() {
        return obtainBitmap();
    }

    @Override
    protected Parcelable onSaveInstanceState() {
        com.wq.app.painter.widget.DrawableViewSaveState state = new com.wq.app.painter.widget.DrawableViewSaveState(super.onSaveInstanceState());
        state.setBoardPage(boardPage);
        return state;
    }

    @Override
    protected void onRestoreInstanceState(Parcelable state) {
        if (!(state instanceof com.wq.app.painter.widget.DrawableViewSaveState)) {
            super.onRestoreInstanceState(state);
        } else {
            com.wq.app.painter.widget.DrawableViewSaveState ss = (com.wq.app.painter.widget.DrawableViewSaveState) state;
            super.onRestoreInstanceState(ss.getSuperState());
            boardPage = ss.getBoardPage();
        }
    }

    @Override
    public void onViewPortChange(RectF currentViewport) {
        com.wq.app.painter.widget.draw.log.LogPrinter.log(" DrawableView onViewPortChange: " + currentViewport);
        gestureCreator.onViewPortChange(currentViewport);
        whitePageDrawer.onViewPortChange(currentViewport);
    }

    @Override
    public void onCanvasChanged(RectF canvasRect) {
        com.wq.app.painter.widget.draw.log.LogPrinter.log(" DrawableView onCanvasChanged: " + canvasRect);
        gestureCreator.onCanvasChanged(canvasRect);
        whitePageDrawer.onCanvasChanged(canvasRect);
    }

    @Override
    public void onGestureCreated(Stroke stroke) {
        boardPage.addLocalStroke(stroke);
    }

    @Override
    public void onStrokeReceived(Stroke stroke) {
        boardPage.addRemoteStroke(stroke);
    }

    @Override
    public void onCurrentGestureChanged(Stroke currStroke) {
        com.wq.app.painter.widget.draw.log.LogPrinter.log(" DrawableView onCurrentGestureChanged: " + currStroke);
    }

    @Override
    public void onScaleChange(float scaleFactor) {
//        gestureScroller.onScaleChange(scaleFactor);
//        gestureCreator.onScaleChange(scaleFactor);
//        whitePageDrawer.onScaleChange(scaleFactor);
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        drawerThread = new StrokeDrawerThread(this, holder);
        drawerThread.setDrawing(true);
        drawerThread.start();
        com.wq.app.painter.widget.draw.log.LogPrinter.log(" DrawableView surfaceCreated: --> ");
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        canvasWidth = width;
        canvasHeight = height;
        com.wq.app.painter.widget.draw.log.LogPrinter.log(" DrawableView surfaceChanged: width: " + width + " height: " + height);
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        // we have to tell thread to shut down & wait for it to finish, or else
        // it might touch the Surface after we return and explode
        boolean retry = true;
        drawerThread.setDrawing(false);
        while (retry) {
            try {
                drawerThread.join();
                retry = false;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
